package org.ncibi.cytoscape.metscape.network;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.ncibi.cytoscape.metscape.data.Attributes;
import org.ncibi.cytoscape.metscape.data.CompoundData;
import org.ncibi.cytoscape.metscape.data.Concept;
import org.ncibi.cytoscape.metscape.data.ConceptData;
import org.ncibi.cytoscape.metscape.data.GeneData;
import org.ncibi.cytoscape.metscape.data.Organism;
import org.ncibi.cytoscape.metscape.plugin.MetScapePlugin;
import org.ncibi.metab.network.MetabolicNetwork;
import org.ncibi.metab.network.edge.MetabolicEdge;
import org.ncibi.metab.network.node.CompoundNode;
import org.ncibi.metab.network.node.GeneNode;
import org.ncibi.metab.network.node.MetabolicNode;

import cytoscape.CyEdge;
import cytoscape.CyNetwork;
import cytoscape.CyNode;
import cytoscape.Cytoscape;
import cytoscape.data.CyAttributesUtils;
import cytoscape.groups.CyGroup;
import cytoscape.groups.CyGroupManager;

public class DefaultNetworkTranslator implements NetworkTranslator
{
	protected String networkName;
	protected MetabolicNetwork sourceNetwork;
	protected Collection<String> compoundIds; 
	protected Collection<Integer> geneIds; 
	protected CompoundData compoundData;
	protected GeneData geneData;
	protected Organism organism;
    protected EdgeTranslator generalEdgeTranslator;
    protected EdgeTranslator reactionEdgeTranslator;
    protected EdgeTranslator enzymeReactionEdgeTranslator;
	protected NodeTranslator compoundNodeTranslator;
	protected NodeTranslator reactionNodeTranslator;
	protected NodeTranslator enzymeNodeTranslator;
	protected NodeTranslator geneNodeTranslator;    
    
    public DefaultNetworkTranslator(String networkName, MetabolicNetwork sourceNetwork, Collection<String> compoundIds, 
    		Collection<Integer> geneIds)
    {
        this.networkName = networkName;
        this.sourceNetwork = sourceNetwork;
        this.compoundIds = compoundIds;
        this.geneIds = geneIds;
        this.compoundData = MetScapePlugin.getPluginData().getCurrentCompoundData();
        this.geneData = MetScapePlugin.getPluginData().getCurrentGeneData();
        this.organism = MetScapePlugin.getPluginData().getCurrentOrganism();
        
        this.generalEdgeTranslator = new GeneralEdgeTranslator();
        this.reactionEdgeTranslator = new ReactionEdgeTranslator();
        this.enzymeReactionEdgeTranslator = new EnzymeReactionEdgeTranslator();
		this.compoundNodeTranslator = new CompoundNodeTranslator(compoundData);
		this.reactionNodeTranslator = new ReactionNodeTranslator();
		this.enzymeNodeTranslator = new EnzymeNodeTranslator();
		this.geneNodeTranslator = new GeneNodeTranslator(geneData,organism);
    }

    @Override
    public CyNetwork toCytoscapeNetwork()
    {
		
    	List<CyNode> nodesList = new ArrayList<CyNode>();
		List<CyEdge> edgesList = new ArrayList<CyEdge>();
 
		
        for (MetabolicNode node : sourceNetwork.getAllNodes())
        {
            CyNode cyNode = getTranslatorForNode(node).toCyNode(node);
            String category = Attributes.node.getStringAttribute(cyNode.getIdentifier(), "Type");
            if( (compoundIds != null && compoundIds.contains(CompoundNode.getCid(node))) ||
            	(geneIds != null && geneIds.contains(GeneNode.getOrganismGeneid(node))))
            	category = "Input " + category;
            Attributes.node.setAttribute
            	(cyNode.getIdentifier(), "Category." + networkName, category);
            Attributes.node.setAttribute(cyNode.getIdentifier(), "isExpansion." + networkName, false);
            Attributes.node.setAttribute(cyNode.getIdentifier(), "isExpansionSeed." + networkName, false);
            Attributes.node.setAttribute(cyNode.getIdentifier(), "isSubnetworkExpansionSeed." + networkName, false);
            nodesList.add(cyNode);
        }
        
        for (MetabolicEdge edge : sourceNetwork.getEdges())
        {
            CyEdge cyEdge = getTranslatorForEdge(edge).toCyEdge(edge);
            Attributes.edge.setAttribute(cyEdge.getIdentifier(), "isExpansion." + networkName, false);
            edgesList.add(cyEdge);
        }
        
        String compoundDataName = compoundData.getName();
		String compoundPvalueColumn = MetScapePlugin.getPluginData().getCompoundPvalueColumn();
		Double compoundPvalueThreshold = MetScapePlugin.getPluginData().getCompoundPvalueThreshold();
		String compoundFoldChangeColumn = MetScapePlugin.getPluginData().getCompoundFoldChangeColumn();
		Double compoundFoldChangeThreshold = MetScapePlugin.getPluginData().getCompoundFoldChangeThreshold();
		String geneDataName = geneData.getName();
		String genePvalueColumn = MetScapePlugin.getPluginData().getGenePvalueColumn();
		Double genePvalueThreshold = MetScapePlugin.getPluginData().getGenePvalueThreshold();
		String geneFoldChangeColumn = MetScapePlugin.getPluginData().getGeneFoldChangeColumn();
		Double geneFoldChangeThreshold = MetScapePlugin.getPluginData().getGeneFoldChangeThreshold();
		Attributes.node.setUserVisible("Compound."+compoundDataName+".hasData",false);
		Attributes.node.setUserVisible("Gene."+geneDataName+".hasData",false);
		
		Map<?,?> compoundPvalues =
			CyAttributesUtils.getAttribute("Compound." + compoundDataName + "." + compoundPvalueColumn, Cytoscape.getNodeAttributes());
		if(compoundPvalues != null)
		for(Object id: compoundPvalues.keySet()) {
			if(compoundPvalueThreshold == null || (Double) compoundPvalues.get(id) <= compoundPvalueThreshold) {
				Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,true);
			}
			else {
				Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,false);
			}
		}
		else {
			Map<?,?> hasData = CyAttributesUtils.getAttribute
				("Compound." + compoundDataName + ".hasData", Cytoscape.getNodeAttributes());
			if(hasData != null)
			for(Object id: hasData.keySet()) {
				if(hasData.get(id).equals(true))
					Attributes.node.setAttribute(id.toString(), "isSignificant."+networkName, true);
			}
		}
		
		Map<?,?> compoundFoldChanges =
			CyAttributesUtils.getAttribute("Compound." + compoundDataName + "." + compoundFoldChangeColumn, Cytoscape.getNodeAttributes());
		if(compoundFoldChanges != null) {
			Double upThreshold = 1.0;
			Double downThreshold = 1.0;
			if(compoundData.getColumnIsSigned().get(compoundFoldChangeColumn)) {
				upThreshold = Math.abs(compoundFoldChangeThreshold);
				downThreshold = -Math.abs(compoundFoldChangeThreshold);
			}
			else {
				if(compoundFoldChangeThreshold >= 1) {
					upThreshold = compoundFoldChangeThreshold;
					downThreshold = 1/compoundFoldChangeThreshold;
				}
				else if(compoundFoldChangeThreshold > 0) {
					upThreshold = 1/compoundFoldChangeThreshold;
					downThreshold = compoundFoldChangeThreshold;
				}
			}
			for(Object id: compoundFoldChanges.keySet()) {
				if((Double) compoundFoldChanges.get(id) >= upThreshold) {
					Attributes.node.setAttribute(id.toString(),"direction."+networkName,"up");
				}
				else if((Double) compoundFoldChanges.get(id) <= downThreshold) {
					Attributes.node.setAttribute(id.toString(),"direction."+networkName,"down");
				}
			}
		}
		
		Map<?,?> genePvalues =
			CyAttributesUtils.getAttribute("Gene." + geneDataName + "." + genePvalueColumn, Cytoscape.getNodeAttributes());
		if(genePvalues != null)
		for(Object id: genePvalues.keySet()) {
			if(genePvalueThreshold == null || (Double) genePvalues.get(id) <= genePvalueThreshold) {
				Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,true);
			}
			else {
				Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,false);
			}
		}
		else {
			Map<?,?> hasData = CyAttributesUtils.getAttribute
				("Gene." + geneDataName + ".hasData", Cytoscape.getNodeAttributes());
			if(hasData != null)
			for(Object id: hasData.keySet()) {
				if(hasData.get(id).equals(true))
					Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,true);
			}
		}
		
		Map<?,?> geneFoldChanges =
			CyAttributesUtils.getAttribute("Gene." + geneDataName + "." + geneFoldChangeColumn, Cytoscape.getNodeAttributes());
		if(geneFoldChanges != null) {
			Double upThreshold = 1.0;
			Double downThreshold = 1.0;
			if(geneData.getColumnIsSigned().get(geneFoldChangeColumn)) {
				upThreshold = Math.abs(geneFoldChangeThreshold);
				downThreshold = -Math.abs(geneFoldChangeThreshold);
			}
			else {
				if(geneFoldChangeThreshold >= 1) {
					upThreshold = geneFoldChangeThreshold;
					downThreshold = 1/geneFoldChangeThreshold;
				}
				else if(geneFoldChangeThreshold > 0) {
					upThreshold = 1/geneFoldChangeThreshold;
					downThreshold = geneFoldChangeThreshold;
				}
			}
			for(Object id: geneFoldChanges.keySet()) {
				if((Double) geneFoldChanges.get(id) >= upThreshold) {
					Attributes.node.setAttribute(id.toString(),"direction."+networkName,"up");
				}
				else if((Double) geneFoldChanges.get(id) <= downThreshold) {
					Attributes.node.setAttribute(id.toString(),"direction."+networkName,"down");
				}
			}
		}
		
        CyNetwork network = Cytoscape.createNetwork(nodesList, edgesList, networkName);
        ConceptData conceptData = MetScapePlugin.getPluginData().getCurrentConceptData();
        for(CyNode cyNode: nodesList) {
        	List<Concept> concepts = conceptData.getConceptMap().get(cyNode.getIdentifier());
        	if(concepts != null)
            for(Concept concept: concepts) {
            	CyGroup group = CyGroupManager.findGroup(network.getIdentifier() + "-" + concept.getConceptName());
            	if(group == null) {
            		group = CyGroupManager.createGroup
            			(network.getIdentifier() + "-" + concept.getConceptName(), null, network);
            		CyNode groupNode = group.getGroupNode();
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.name", concept.getConceptName());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numUniqueGenes", concept.getNumUniqueGenes());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork", 0);
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.pvalue", concept.getPvalue());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.fdr", concept.getFdr());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.direction", concept.getDirection());
            	}
            	if(Attributes.node.getStringAttribute(cyNode.getIdentifier(), "Type").equals("Gene")) {
            		CyNode groupNode = group.getGroupNode();
            		Integer numGenesInNetwork = Attributes.node.getIntegerAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork");
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork", ++numGenesInNetwork);
            	}
            	group.addNode(cyNode);
            }
        }
        Attributes.network.setAttribute(network.getIdentifier(), "NetworkType", sourceNetwork.getNetworkType().toString());
        Attributes.network.setAttribute(network.getIdentifier(), "Taxid", MetScapePlugin.getPluginData().getCurrentOrganism().getTaxid());
        Attributes.network.setAttribute(network.getIdentifier(), "CompoundData", 
        		MetScapePlugin.getPluginData().getCompoundDataStore().indexOf(MetScapePlugin.getPluginData().getCurrentCompoundData()));
        Attributes.network.setAttribute(network.getIdentifier(), "CompoundPvalueColumn", compoundPvalueColumn);
        Attributes.network.setAttribute(network.getIdentifier(), "CompoundPvalueThreshold", compoundPvalueThreshold);
        Attributes.network.setAttribute(network.getIdentifier(), "CompoundFoldChangeColumn", compoundFoldChangeColumn);
        Attributes.network.setAttribute(network.getIdentifier(), "CompoundFoldChangeThreshold", compoundFoldChangeThreshold);
        Attributes.network.setAttribute(network.getIdentifier(), "GeneData", 
        		MetScapePlugin.getPluginData().getGeneDataStore().indexOf(MetScapePlugin.getPluginData().getCurrentGeneData()));
        Attributes.network.setAttribute(network.getIdentifier(), "GenePvalueColumn", genePvalueColumn);
        Attributes.network.setAttribute(network.getIdentifier(), "GenePvalueThreshold", genePvalueThreshold);
        Attributes.network.setAttribute(network.getIdentifier(), "GeneFoldChangeColumn", geneFoldChangeColumn);
        Attributes.network.setAttribute(network.getIdentifier(), "GeneFoldChangeThreshold", geneFoldChangeThreshold);
        Attributes.network.setAttribute(network.getIdentifier(), "ConceptData", 
        		MetScapePlugin.getPluginData().getConceptDataStore().indexOf(MetScapePlugin.getPluginData().getCurrentConceptData()));
        return network;
    }
    
    protected NodeTranslator getTranslatorForNode(MetabolicNode node) {
    	switch (node.getType()) {
    	case COMPOUND:
    		return compoundNodeTranslator;
    	case REACTION:
    		return reactionNodeTranslator;
    	case ENZYME:
    		return enzymeNodeTranslator;
    	case GENE:
    		return geneNodeTranslator;
    	default:
    		return null;
    	}
    }
    
    protected EdgeTranslator getTranslatorForEdge(MetabolicEdge edge) {
    	switch(edge.getType()) {
    	case REACTION:
    		return reactionEdgeTranslator;
    	case ENZYME_REACTION:
    		return enzymeReactionEdgeTranslator;
    	default:
    		return generalEdgeTranslator;
    	}
    }
    
}
